using System;
using System.Collections.Generic;

namespace RGuang.Kit
{
    public interface IState<IStateID>
    {
        IStateID StateId { get; }
        void DestroyState();
        bool Condition();
        void Enter();
        void Exit();
        void OnGUI();
        void Update();
        void FixedUpdate();

    }


    /// <summary>
    /// 简易状态机
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class FSM<IStateID>
    {
        /// <summary>
        /// 状态缓存
        /// key：T = 状态ID
        /// value：IState = 状态
        /// </summary>
        private Dictionary<IStateID, IState<IStateID>> m_States = new Dictionary<IStateID, IState<IStateID>>();
        /// <summary>
        /// 携带数据
        /// </summary>
        private Dictionary<string, object> m_FsmData = new Dictionary<string, object>();
        /// <summary>
        /// 状态切换
        /// args1 = 切换前的状态ID
        /// args2 = 切换后的状态ID
        /// </summary>
        private Action<IStateID, IStateID> m_OnStateChanged = (fromState, toState) => { };
        /// <summary>
        /// 当前状态
        /// </summary>
        public IState<IStateID> CurrentState { get; private set; }
        /// <summary>
        /// 上一个状态ID
        /// </summary>
        public IState<IStateID> PreviousState { get; private set; }
        /// <summary>
        /// 当前状态经过的帧数
        /// </summary>
        public long FrameCountOfCurrentState { get; private set; }
        /// <summary>
        /// 当前状态经过的秒数
        /// </summary>
        public float SecondsOfCurrentState { get; private set; }

        /// <summary>
        /// 检测是否存在指定状态
        /// </summary>
        /// <param name="id">指定状态id</param>
        /// <param name="existState">获取到存在的状态</param>
        /// <returns>如果存在返回true，否则返回false</returns>
        public bool HasState(IStateID id, out IState<IStateID> existState)
        {
            if (m_States.TryGetValue(id, out existState))
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 添加一个状态
        /// </summary>
        /// <param name="state">状态</param>
        public void AddState(IState<IStateID> state) => m_States.Add(state.StateId, state);
        /// <summary>
        /// 添加一个状态
        /// </summary>
        /// <param name="state">状态</param>
        /// <returns>添加成功返回true，如果id已存在则不添加并返回false</returns>
        public bool TryAddState(IState<IStateID> state) => m_States.TryAdd(state.StateId, state);
        /// <summary>
        /// 启动状态
        /// ps：如果指定的状态ID不存在，则不做任何操作
        /// </summary>
        /// <param name="stateID">状态ID</param>
        public void StartState(IStateID stateID)
        {
            if (m_States.TryGetValue(stateID, out var state))
            {
                CurrentState?.Exit();
                PreviousState = CurrentState;
                CurrentState = state;
                FrameCountOfCurrentState = 1;
                SecondsOfCurrentState = 0.0f;
                state.Enter();
            }
        }
        /// <summary>
        /// 状态切换到目标状态
        /// ps：如果 不存在的目标状态ID || 当前状态==null || 目标状态的Condition返回false，则不做任何操作
        /// ps：如果 当前状态==目标状态，则调用当前状态的ReEnter方法
        /// </summary>
        /// <param name="targetID">从当前状态要切换的目标状态ID</param>
        public void ChangeState(IStateID targetID)
        {
            if (CurrentState == null) return;

            if (m_States.TryGetValue(targetID, out var state))
            {
                if (state.Condition())
                {
                    CurrentState.Exit();
                    PreviousState = CurrentState;
                    CurrentState = state;
                    m_OnStateChanged?.Invoke(PreviousState.StateId, CurrentState.StateId);
                    FrameCountOfCurrentState = 1;
                    SecondsOfCurrentState = 0.0f;
                    CurrentState.Enter();
                }
            }
        }

        public void SetFsmData(string key, object value)
        {
            if (m_FsmData.ContainsKey(key))
            {
                m_FsmData.Add(key, null);
            }

            m_FsmData[key] = value;
        }
        public object GetFsmData(string key)
        {
            if (m_FsmData.TryGetValue(key, out var value)) { }

            return value;
        }
        public void RemoveFsmData(string key)
        {
            m_FsmData.Remove(key);
        }

        /// <summary>
        /// 设置回调：状态变更时回调
        /// </summary>
        /// <param name="onStateChangedCallback"></param>
        public void SetCallbackOnStateChanged(Action<IStateID, IStateID> onStateChangedCallback) => m_OnStateChanged += onStateChangedCallback;

        /// <summary>
        /// 撤销回调：状态变更时回调
        /// </summary>
        /// <param name="onStateChangedCallback"></param>
        public void UnSetCallbackOnStateChanged(Action<IStateID, IStateID> onStateChangedCallback) => m_OnStateChanged -= onStateChangedCallback;


        public void OnGUI()
        {
            CurrentState?.OnGUI();
        }
        public void Update()
        {
            CurrentState?.Update();
            FrameCountOfCurrentState++;
            SecondsOfCurrentState += UnityEngine.Time.deltaTime;
        }

        public void FixedUpdate()
        {
            CurrentState?.FixedUpdate();
        }

        public void Clear()
        {
            CurrentState = null;
            PreviousState = null;
            var iterator = m_States.GetEnumerator();
            while (iterator.MoveNext())
            {
                iterator.Current.Value.DestroyState();
            }
            m_States.Clear();
            m_FsmData.Clear();
            m_OnStateChanged = (fromState, toState) => { };
        }


    }


    /// <summary>
    /// 抽象状态
    /// </summary>
    /// <typeparam name="TStateId"></typeparam>
    public abstract class AbstractState<TStateId> : IState<TStateId>
    {
        protected FSM<TStateId> m_FSM;
        protected TStateId m_StateId;
        public AbstractState(TStateId stateId, FSM<TStateId> fsm)
        {
            m_StateId = stateId;
            m_FSM = fsm;
        }

        #region Interface IState Implement
        public TStateId StateId => m_StateId;
        void IState<TStateId>.DestroyState() => OnDestroyState();
        bool IState<TStateId>.Condition() => OnCondition();
        void IState<TStateId>.Enter() => OnEnter();
        void IState<TStateId>.Exit() => OnExit();
        void IState<TStateId>.OnGUI() => OnGUI();
        void IState<TStateId>.Update() => OnUpdate();
        void IState<TStateId>.FixedUpdate() => OnFixedUpdate();
        #endregion

        protected virtual void OnDestroyState() { }
        protected virtual bool OnCondition() => true;
        protected virtual void OnEnter() { }
        protected virtual void OnReEnter() { }
        protected virtual void OnExit() { }
        protected virtual void OnGUI() { }
        protected virtual void OnUpdate() { }
        protected virtual void OnFixedUpdate() { }

    }



}
